#region Using References

using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using Beaker.OpenCube.Nbt;

#endregion

namespace Beaker.OpenCube.Specs.Builders
{
	[ExcludeFromCodeCoverage]
	public sealed class NbtStreamBuilder
		: Builder<Stream>
	{
		private readonly MemoryStream _stream = new MemoryStream();
		private readonly BinaryWriter _writer;

		public NbtStreamBuilder()
		{
			_writer = new BinaryWriter( _stream );
		}

		public NbtStreamBuilder Append( NbtTag tag )
		{
			_writer.Write( (byte)tag );
			return this;
		}

		public NbtStreamBuilder Append( string name )
		{
			byte[] data = Encoding.UTF8.GetBytes( name );

			// big endian write of length
			Append( (short)data.Length );

			// write utf8 string
			_writer.Write( data, 0, data.Length );
			return this;
		}

		public NbtStreamBuilder Append( NbtTag tag, string name )
		{
			Append( tag );
			Append( name );

			return this;
		}

		public NbtStreamBuilder Append( byte value )
		{
			_writer.Write( value );
			return this;
		}

		public NbtStreamBuilder Append( sbyte value )
		{
			_writer.Write( value );
			return this;
		}

		public NbtStreamBuilder Append( short value )
		{
			byte[] data = BitConverter.GetBytes( value );

			// reverse if system is little endian, because big endian is expected for Nbt format
			if( BitConverter.IsLittleEndian )
				Array.Reverse( data );

			_writer.Write( data, 0, 2 );

			return this;
		}

		public NbtStreamBuilder Append( int value )
		{
			byte[] data = BitConverter.GetBytes( value );

			// reverse if system is little endian, because big endian is expected for Nbt format
			if( BitConverter.IsLittleEndian )
				Array.Reverse( data );

			_writer.Write( data, 0, 4 );

			return this;
		}

		public NbtStreamBuilder Append( long value )
		{
			byte[] data = BitConverter.GetBytes( value );

			// reverse if system is little endian, because big endian is expected for Nbt format
			if( BitConverter.IsLittleEndian )
				Array.Reverse( data );

			_writer.Write( data, 0, 8 );

			return this;
		}

		public NbtStreamBuilder Append( float value )
		{
			byte[] data = BitConverter.GetBytes( value );

			// reverse if system is little endian, because big endian is expected for Nbt format
			if( BitConverter.IsLittleEndian )
				Array.Reverse( data );

			_writer.Write( data, 0, 4 );

			return this;
		}

		public NbtStreamBuilder Append( double value )
		{
			byte[] data = BitConverter.GetBytes( value );

			// reverse if system is little endian, because big endian is expected for Nbt format
			if( BitConverter.IsLittleEndian )
				Array.Reverse( data );

			_writer.Write( data, 0, 8 );

			return this;
		}

		public NbtStreamBuilder Append( byte[] value )
		{
			_writer.Write( value );
			return this;
		}

		public NbtStreamBuilder Append( sbyte[] value )
		{
			// convert sbyte to byte
			var buffer = new byte[value.Length];
			Buffer.BlockCopy( value, 0, buffer, 0, value.Length );

			// write
			_writer.Write( buffer );
			return this;
		}

		protected override Stream OnBuild()
		{
			_stream.Position = 0;
			return _stream;
		}
	}
}